import { Component, OnDestroy, OnInit, ViewChild } from '@angular/core';
import { finalize } from "rxjs/operators";
import { ScanningMetrics, Triggers } from '../../config/config';
import { ErrorHandler } from '../../../../shared/units/error-handler';
import { TranslateService } from '@ngx-translate/core';
import { ScanAllRepoService } from './scanAll.service';
import { OriginCron } from '../../../../shared/services';
import { CronScheduleComponent } from "../../../../shared/components/cron-schedule";
import { DATABASE_UPDATED_PROPERTY, VULNERABILITY_SCAN_STATUS } from "../../../../shared/units/utils";
import { DatePipe } from "@angular/common";
import { errorHandler } from "../../../../shared/units/shared.utils";
import { ScanAllService } from "../../../../../../ng-swagger-gen/services/scan-all.service";

const SCHEDULE_TYPE_NONE = "None";
const TIMEOUT: number = 5000;

@Component({
    selector: 'vulnerability-config',
    templateUrl: './vulnerability-config.component.html',
    styleUrls: ['./vulnerability-config.component.scss']
})
export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
    onGoing: boolean;
    originCron: OriginCron;
    schedule: any;
    onSubmitting: boolean = false;
    openState: boolean = false;
    getLabelCurrent: string;

    @ViewChild(CronScheduleComponent)
    CronScheduleComponent: CronScheduleComponent;
    gettingMetrics: boolean;
    private _scanningMetrics: ScanningMetrics;
    totalWidth: number = 200;
    i18nKeyMap = {
        "Pending": "CONFIG.SCANNING.STATUS.PENDING",
        "Running": "CONFIG.SCANNING.STATUS.RUNNING",
        "Stopped": "CONFIG.SCANNING.STATUS.STOPPED",
        "Error": "CONFIG.SCANNING.STATUS.ERROR",
        "Success": "CONFIG.SCANNING.STATUS.SUCCESS",
        "Scheduled": "CONFIG.SCANNING.STATUS.SCHEDULED"
    };
    updatedTimeStr: string;
    onGettingUpdatedTimeStr: boolean;
    hasDefaultScanner: boolean = false;
    private _timeout: any;

    constructor(
        private scanningService: ScanAllRepoService,
        private newScanAllService: ScanAllService,
        private errorHandlerEntity: ErrorHandler,
        private translate: TranslateService,
    ) {
    }

    get scanningMetrics(): ScanningMetrics {
        return this._scanningMetrics;
    }

    set scanningMetrics(metrics: ScanningMetrics) {
        this._scanningMetrics = metrics;
    }

    get scanAvailable(): boolean {
        return !this.onSubmitting
            && !this.gettingMetrics
            && !this.isOnScanning()
            && this.hasDefaultScanner;
    }

    getScanText() {
        this.translate.get('CONFIG.SCANNING.SCHEDULE_TO_SCAN_ALL').subscribe((res: string) => {
            this.getLabelCurrent = res;
        });
    }

    getSchedule() {
        this.onGoing = true;
        this.scanningService.getSchedule()
            .pipe(finalize(() => {
                this.onGoing = false;
            }))
            .subscribe(schedule => {
                this.initSchedule(schedule);
            }, error => {
                this.errorHandlerEntity.error(error);
            });
    }

    getScanners() {
        this.onGettingUpdatedTimeStr = true;
        this.scanningService.getScanners()
            .subscribe(scanners => {
                let flag = false;
                if (scanners && scanners.length) {
                    scanners.forEach(scanner => {
                        if (scanner.is_default) {
                            flag = true;
                            this.getMetrics();
                            this.getSchedule();
                            this.getScannerMetadata(scanner.uuid);
                        }
                    });
                }
                if (flag) {
                    this.hasDefaultScanner = true;
                }
                if (!flag) {
                    this.onGettingUpdatedTimeStr = false;
                    this.translate.get("SCANNER.NO_DEFAULT_SCANNER")
                        .subscribe(res => {
                                this.errorHandlerEntity.warning(res);
                            }
                        );
                }
            }, error => {
                this.onGettingUpdatedTimeStr = false;
            });
    }

    getScannerMetadata(uid: string) {
        this.scanningService.getScannerMetadata(uid)
            .pipe(finalize(() => this.onGettingUpdatedTimeStr = false))
            .subscribe(metadata => {
                if (metadata && metadata.properties) {
                    for (let key in metadata.properties) {
                        if (key === DATABASE_UPDATED_PROPERTY && metadata.properties[key]) {
                            this.updatedTimeStr = new DatePipe('en-us').transform(metadata.properties[key], 'short');
                        }
                    }
                }
            });
    }

    public initSchedule(schedule: any) {
        if (schedule && schedule.schedule !== null) {
            this.schedule = schedule;
            this.originCron = this.schedule.schedule;
        } else {
            this.originCron = {
                type: SCHEDULE_TYPE_NONE,
                cron: ''
            };
        }
    }

    ngOnInit(): void {
        this.getScanText();
        this.getScanners();
    }

    ngOnDestroy() {
        if (this._timeout) {
            clearTimeout(this._timeout);
            this._timeout = null;
        }
    }

    isOnScanning(): boolean {
        return this.scanningMetrics
            && this.scanningMetrics.ongoing;
    }

    getMetrics() {
        this.gettingMetrics = true;
        this.scanningService.getMetrics()
            .pipe(finalize(() => this.gettingMetrics = false))
            .subscribe(response => {
                if (response) {
                    if (response.ongoing) {
                        if (this._timeout) {
                            clearTimeout(this._timeout);
                            this._timeout = null;
                        }
                        if (!this._timeout) {
                            this._timeout = setTimeout(() => {
                                this.getMetrics();
                            }, TIMEOUT);
                        }
                    }
                    this.scanningMetrics = response;
                }
            });
    }

    getI18nKey(str: string): string {
        if (str && this.i18nKeyMap[str]) {
            return this.i18nKeyMap[str];
        }
        return str;
    }

    errorWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.ERROR]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.ERROR] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }

    finishedWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.SUCCESS]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.SUCCESS] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }

    runningWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.RUNNING]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.RUNNING] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }

    abortWidth() {
        if (this.scanningMetrics
            && this.scanningMetrics.metrics
            && this.scanningMetrics.total
            && this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED]) {
            return this.scanningMetrics.metrics[VULNERABILITY_SCAN_STATUS.STOPPED] /
                this.scanningMetrics.total * this.totalWidth + 'px';
        }
        return '0';
    }

    scanNow(): void {
        if (this.onSubmitting) {
            return; // Aoid duplicated submitting
        }

        if (!this.scanAvailable) {
            return; // Aoid page hacking
        }

        this.onSubmitting = true;
        this.scanningService.manualScan()
            .pipe(finalize(() => this.onSubmitting = false))
            .subscribe(() => {
                    this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_SUCCESS").subscribe((res: string) => {
                        this.errorHandlerEntity.info(res);
                    });
                    // reset metrics and then get new metrics
                    this.scanningMetrics = null;
                    this.getMetrics();
                }
                , error => {
                    if (error && error.status && error.status === 412) {
                        this.translate.get("CONFIG.SCANNING.TRIGGER_SCAN_ALL_FAIL",
                            {error: '' + errorHandler(error)}).subscribe((res: string) => {
                            this.errorHandlerEntity.error(res);
                        });
                    } else {
                        this.errorHandlerEntity.error(error);
                    }
                });
    }

    reset(cron): void {
        this.schedule = {
            schedule: {
                type: this.CronScheduleComponent.scheduleType,
                cron: cron
            }
        };
    }

    saveSchedule(cron: string): void {
        let schedule = this.schedule;
        if (schedule && schedule.schedule && schedule.schedule.type !== SCHEDULE_TYPE_NONE) {
            this.scanningService.putSchedule(this.CronScheduleComponent.scheduleType, cron)
                .subscribe(response => {
                        this.translate
                            .get("CONFIG.SAVE_SUCCESS")
                            .subscribe((res) => {
                                this.errorHandlerEntity.info(res);
                                this.CronScheduleComponent.resetSchedule();
                            });
                        this.reset(cron);
                    },
                    error => {
                        this.errorHandlerEntity.error(error);
                    }
                );
        } else {
            this.scanningService.postSchedule(this.CronScheduleComponent.scheduleType, cron)
                .subscribe(response => {
                        this.translate.get("CONFIG.SAVE_SUCCESS").subscribe((res) => {
                            this.errorHandlerEntity.info(res);
                            this.CronScheduleComponent.resetSchedule();
                        });
                        this.reset(cron);
                    },
                    error => {
                        this.errorHandlerEntity.error(error);
                    }
                );
        }
    }

    isError(status: string): boolean {
        return status === VULNERABILITY_SCAN_STATUS.ERROR;
    }

    isFinished(status: string): boolean {
        return status === VULNERABILITY_SCAN_STATUS.SUCCESS;
    }

    isInProgress(status: string): boolean {
        return status === VULNERABILITY_SCAN_STATUS.RUNNING;
    }

    isAborted(status: string): boolean {
        return status === VULNERABILITY_SCAN_STATUS.STOPPED;
    }

    isManual() {
        return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.MANUAL;
    }

    isSchedule() {
        return this.scanningMetrics && this.scanningMetrics.trigger === Triggers.SCHEDULE;
    }

    scanOrStop() {
        if (this.isOnScanning()) {
            this.stopNow();
        } else {
            this.scanNow();
        }
    }

    stopNow() {
        this.onSubmitting = true;
        this.newScanAllService.stopScanAll()
            .pipe(finalize(() => this.onSubmitting = false))
            .subscribe(res => {
                this.errorHandlerEntity.info('CONFIG.SCANNING.STOP_SCAN_ALL_SUCCESS');
            }, error => {
                this.errorHandlerEntity.error(error);
            });
    }
}
